package com.atguigu.tingshu.account.receiver;

import com.atguigu.tingshu.account.service.MqOpsService;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.rabbit.service.RabbitService;
import com.rabbitmq.client.Channel;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.io.IOException;

/**
 * Description:
 * account微服务的消费者类
 * @author Yanxinhao on 2025/1/13
 */
@Component
@Slf4j
public class UserAccountReceiver {

    @Autowired
    private MqOpsService mqOpsService;

    @Autowired
    private RabbitService rabbitService;

    @Autowired
    private StringRedisTemplate redisTemplate;


    /**
     * 用户注册的时候，初始化用户account表
     * @param context
     * @param message
     * @param channel
     */
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = MqConst.QUEUE_ACCOUNT_INIT,durable = "true"),
            exchange = @Exchange(value = MqConst.EXCHANGE_ACCOUNT),
            key = MqConst.ROUTING_INIT_ACCOUNT))
    @SneakyThrows //在编译期间忽略掉异常，在执行过程中如果有异常，那么就会抛出异常
    public void listenAccountInit(String context, Message message, Channel channel){
        //1.若传来的消息为空，那么就没有必要继续执行
        if (StringUtils.isEmpty(context)){
            return;
        }
        //2.进行账户account的初始化 ——消费消息
        mqOpsService.userAccountInit(context);

        //3.消费完消息了，进行手动应答
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);

    }


    /**
     * 用户进行消费活动的时候，操作account表，进行余额扣减
     * @param content
     * @param message
     * @param channel
     */
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = MqConst.QUEUE_ACCOUNT_MINUS),
            exchange = @Exchange(value = MqConst.EXCHANGE_ACCOUNT),
            key = MqConst.ROUTING_ACCOUNT_MINUS))
    @SneakyThrows
    public void minusAccountMoney(String content, Message message, Channel channel) {
        //1.判断是否有消息传递，如果消息为空就直接返回
        if (StringUtils.isEmpty(content)){
            return;
        }
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        try {

            //2.消费消息，进行余额扣减
            mqOpsService.minusAccountMoney(content);

            //3.消息消费完毕，发送消息给下游，通知进行本地消费日志表的修改
            rabbitService.sendMessage(MqConst.EXCHANGE_LOCAL_MSG,MqConst.ROUTING_LOCAL_MSG,content);

            //4.消息消费完毕，手动应答
            channel.basicAck(deliveryTag,false);

        } catch (GuiguException e) {
            //5.如果我们捕获到的是guigu异常，那么我们就进行重试，重试三次
            String retryMsgKey = "retry:msg:" + content;
            Long count = redisTemplate.opsForValue().increment(retryMsgKey);
            if (count >= 3){
                log.error("消息重试了{}次，异常原因：{}，请人工排查", count, e.getMessage());
                channel.basicNack(deliveryTag,false,false);
            } else {
                log.error("消息重试了{}次", count);
                channel.basicNack(deliveryTag,false,true);
            }
        } catch (Exception e){
            log.error("手动应答期间，网络出现了抖动，但是业务已经被成功执行过，消息直接丢掉");
            channel.basicNack(deliveryTag, false, false);
        }
    }



    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = MqConst.QUEUE_ACCOUNT_UNLOCK),
            exchange = @Exchange(value = MqConst.EXCHANGE_ACCOUNT),
            key = MqConst.ROUTING_ACCOUNT_UNLOCK))
    @SneakyThrows
    public void unlockAccountMoney(String content, Message message, Channel channel) {
        //1.判断是否有消息传递，如果消息为空就直接返回
        if (StringUtils.isEmpty(content)){
            return;
        }
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        try {

            //2.消费消息，进行扣减余额补偿
            mqOpsService.unlockAccountMoney(content);

            //3.消息消费完毕，发送消息给下游，通知进行本地消费日志表的修改
            rabbitService.sendMessage(MqConst.EXCHANGE_LOCAL_MSG,MqConst.ROUTING_LOCAL_MSG,content);

            //4.消息消费完毕，手动应答
            channel.basicAck(deliveryTag,false);

        } catch (GuiguException e) {
            //5.如果我们捕获到的是guigu异常，那么我们就进行重试，重试三次
            String retryMsgKey = "retry:msg:" + content;
            Long count = redisTemplate.opsForValue().increment(retryMsgKey);
            if (count >= 3){
                log.error("消息重试了{}次，异常原因：{}，请人工排查", count, e.getMessage());
                channel.basicNack(deliveryTag,false,false);
            } else {
                log.error("消息重试了{}次", count);
                channel.basicNack(deliveryTag,false,true);
            }
        } catch (Exception e){
            log.error("手动应答期间，网络出现了抖动，但是业务已经被成功执行过，消息直接丢掉");
            channel.basicNack(deliveryTag, false, false);
        }
    }

}